﻿using System;
using System.IO;
using System.Collections.Generic;

namespace Game.Lib.CSG
{
    public class Arc : Shape1D
    {
        /// <summary>
        /// Center of the circle this arc lies on
        /// </summary>
        public Vector2 Center;

        /// <summary>
        /// The radius of the circle this arc lies on
        /// </summary>
        public float Radius;

        /// <summary>
        /// The angular start of the arc
        /// </summary>
        public float AngleStart
        {
            get
            {
                return _AngleStart;
            }
            set
            {
                if (value < 0f || value > Utils.TWO_PI) throw new ArgumentOutOfRangeException("The start angle must be between 0 and TWO_PI");
                _AngleStart = value;
            }
        }

        /// <summary>
        /// The angular end of the arc
        /// </summary>
        public float AngleEnd
        {
            get
            {
                return _AngleEnd;
            }
            set
            {
                if (Math.Abs(value) > Utils.TWO_PI) throw new ArgumentOutOfRangeException("The end angle must be between -TWO_PI and TWO_PI");
                _AngleEnd = value;
            }
        }

        public float Angle
        {
            get
            {
                //AngleStart > 0f
                //There are two possibilities, AngleEnd >= 0f(long arc) or AngleEnd < 0f(short arc)
                if (AngleEnd >= 0f) return AngleEnd - AngleStart;
                else return (float)Math.Abs(AngleEnd) + AngleStart;
            }
        }

        public override float Length
        {
            get { return (float)(4.0 * Math.PI * Math.PI * Radius / Angle); }
        }

        public override List<Vector> CriticalPoints
        {
            get
            {
                List<Vector> ret = new List<Vector>();
                ret.Add(Center);
                if (Math.Abs(Angle - Utils.TWO_PI) <= Utils.Epsilon)
                {
                    ret.Add(Center + Vector2.FromPolar(Radius, 0f));
                    ret.Add(Center + Vector2.FromPolar(Radius, Utils.PI_BY_TWO));
                    ret.Add(Center + Vector2.FromPolar(Radius, Utils.PI));
                    ret.Add(Center + Vector2.FromPolar(Radius, Utils.PI + Utils.PI_BY_TWO));
                }
                else
                {
                    ret.Add(Start);
                    ret.Add(End);
                }
                return ret;
            }
        }

        public Vector2 Start
        {
            get
            {
                return Center + Vector2.FromPolar(Radius, AngleStart);
            }
        }

        public Vector2 End
        {
            get
            {
                return Center + Vector2.FromPolar(Radius, AngleEnd);
            }
        }

        private float _AngleStart;
        private float _AngleEnd;

        public Arc()
        {
            Center = Vector2.Zero;
            Radius = 1f;
            AngleStart = 0f;
            AngleEnd = Utils.TWO_PI;
        }

        public Arc(Vector2 pos, float r)
            : this()
        {
            Center = pos;
            Radius = r;
        }

        public Arc(Vector2 pos, float r, float start, float end)
            : this(pos, r)
        {
            AngleStart = start;
            AngleEnd = end;
        }


        public override void Serialize(Stream s)
        {
            Center.Serialize(s);
            byte[] buf = BitConverter.GetBytes(Radius);
            if(!BitConverter.IsLittleEndian) Array.Reverse(buf);
            s.Write(buf, 0, 4);

            buf = BitConverter.GetBytes(AngleStart);
            if (!BitConverter.IsLittleEndian) Array.Reverse(buf);
            s.Write(buf, 0, 4);

            buf = BitConverter.GetBytes(AngleEnd);
            if (!BitConverter.IsLittleEndian) Array.Reverse(buf);
            s.Write(buf, 0, 4);
        }

        public override void Deserialize(Stream s)
        {
            Center.Deserialize(s);
            byte[] buf = new byte[12];
            if (s.Read(buf, 0, 12) != 12) throw new EndOfStreamException();
            if (!BitConverter.IsLittleEndian)
            {
                Array.Reverse(buf, 0, 4);
                Array.Reverse(buf, 4, 4);
                Array.Reverse(buf, 8, 4);
            }
            Radius = BitConverter.ToSingle(buf, 0);
            AngleStart = BitConverter.ToSingle(buf, 4);
            AngleEnd = BitConverter.ToSingle(buf, 8);
        }
    }
}
